home *** CD-ROM | disk | FTP | other *** search
/ BUG 1 / BUGCD1996_0708.ISO / mac / Alati / Screen Savers / DarkSide of the Mac 4.2.sea / DarkSide of the Mac 4.2 / SampleFaders / Morpion.c < prev    next >
Text File  |  1994-08-18  |  10KB  |  326 lines

  1. /*
  2.     Morpion (French) == 5 In A Row (English)
  3.     The first one who puts 5 markers in a row (horizontal, vertical or diagonal) wins. The computer plays against itself.
  4.     
  5.     This small fader is intended as a code example. It is based on FaderShell, written by Tom Dowdy.
  6.     This code is hereby placed into the public domain. Use it as a template to write your faders!
  7.         
  8.     You can use global variables, they are stored as an offset from A4 using THINK's SetupA4/RestoreA4 procedures.
  9.     
  10.     This fader also demonstrates the use of callback routines by playing sound. Note that in order to play sound, you must
  11.     request a sound channel from DarkSide by creating a 'Chnl' 0 resource. 
  12. */
  13.  
  14. #include <Memory.h>
  15. #include <Windows.h>
  16. #include <Dialogs.h>
  17. #include <Errors.h>
  18. #include <ToolUtils.h>
  19.  
  20. #include "Fader.h"                                // include DarkSide's interface
  21.  
  22. #ifdef THINK_C
  23.     #define QUICKDRAWBLACK    (qd.black)
  24.     #define QUICKDRAWWHITE    (qd.white)
  25. #else
  26.     #define QUICKDRAWBLACK    (&qd.black)
  27.     #define QUICKDRAWWHITE    (&qd.white)
  28. #endif
  29.  
  30. // Constants. These are specific to Morpion, you can throw them away if you are writing a new fader.
  31.  
  32. #define    border        2
  33.  
  34. #define    empty        0
  35. #define    player1        1
  36. #define    player2        2
  37.  
  38. // Global variables, referenced using A4. Also Morpion-specific.
  39.  
  40. short        xloc, yloc;                        // starting location of the grid
  41. short         xsize, ysize;                        // Width and height of grid
  42. short        xc, yc;                            // Pixel coordinates of topleft corner
  43. Handle        grid, points, possible;    
  44. short        value[5][5];
  45. short        turn;                                // Tells who's up
  46. Boolean        draw, winner;                        // End-of-game flags
  47. short        unit;                                // Size of squares
  48. long        nextDraw;                        // TickCount of the next time to draw
  49.  
  50. // grid is considered as a two dimensional array 0..xsize-1, 0..ysize-1 of chars
  51. // points as an array 0..xsize-1, 0..ysize-1 of shorts
  52. // possible as an array 0..xsize*ysize-1 of points
  53.  
  54. #define    Grid(x, y)        ((char*) (*grid)) [xsize * y + x]
  55. #define    Points(x, y)    ((short*) (*points)) [xsize * y + x]
  56. #define    Possible(x)    ((Point*) (*possible)) [x]
  57.  
  58. void StartGame (void)
  59. {
  60.     long        ix;
  61.     char         *p;
  62.  
  63.     turn = player1;                                                // white plays first
  64.     draw = winner = false;                                        
  65.     for (p = *grid, ix = xsize*ysize; ix; p++, ix--) *p = empty;            // clear the grid
  66. }
  67.  
  68. // Called when fader is starting up, before window has been created.
  69. // We initialize our global variables here.
  70.  
  71. OSErr    PreflightFader(MachineInfoPtr machineInfo, long *minTicks, long *maxTicks)
  72. {    
  73.     Rect        bounds;
  74.     long        gridsize;
  75.     short    a, b;
  76.     
  77.     *minTicks = 1;                                    // Tell DarkSide how often we want to be called.
  78.     *maxTicks = 15;
  79.     
  80.     // The rest is specific to Morpion.
  81.     
  82.     unit = machineInfo->faderSettings->theShorts[1];                    // read the settings
  83.     bounds = machineInfo->theScreens[0].bounds;                        // compute the size of our grid
  84.     xloc = bounds.left;
  85.     yloc = bounds.top;
  86.     xsize = (bounds.right - bounds.left - 6 * unit) / unit;
  87.     ysize = (bounds.bottom - bounds.top - 6 * unit) / unit;
  88.     xc = bounds.left + (bounds.right - bounds.left - unit * xsize) / 2;
  89.     yc = bounds.top + (bounds.bottom - bounds.top - unit * ysize) / 2;
  90.     
  91.     if (!(grid = BestNewHandle (gridsize = xsize * ysize)))                // allocate memory for the grid
  92.         return memFullErr;
  93.     if (!(possible = BestNewHandle (4*gridsize))) {                        // allocate memory for the 'possible' array
  94.         DisposHandle(grid);
  95.         return memFullErr;
  96.     }
  97.     if (!(points = BestNewHandle (2*gridsize)))    {                    // allocate memory for the 'points' array
  98.         DisposHandle(possible);
  99.         DisposHandle(grid);
  100.         return memFullErr;
  101.     }
  102.     
  103.     for (a = 0; a < 5; a++)                                        // initialize the 'value' array
  104.         for (b = 0; b < 5; b++)
  105.             value [a][b] = 0;
  106.     value [0][0] = 10; value [0][1] = 20; value [0][2] = 80; value [0][3] = 300; value [0][4] = 2000;
  107.     value [1][0] = 20; value [2][0] = 70; value [3][0] = 400; value [4][0] = 4000;
  108.     
  109.     StartGame();
  110.  
  111.     return noErr;
  112. }
  113.  
  114. // Called when fader is starting up, after window has been created. We usually erase the screens here.
  115. // We then draw the grid.
  116.  
  117. OSErr    InitializeFader(MachineInfoPtr machineInfo)
  118. {
  119.     short        screenIndex;
  120.     short        ix;
  121.  
  122.     // store the local coordinates of our windows
  123.     xloc = machineInfo->theScreens[0].bounds.left;
  124.     yloc = machineInfo->theScreens[0].bounds.top;
  125.  
  126.     PenPat(QUICKDRAWBLACK);                                            // erase the screens.
  127.     for (screenIndex = 0; screenIndex < machineInfo->numScreens; screenIndex++) 
  128.         PaintRect(&machineInfo->theScreens[screenIndex].bounds);
  129.         
  130.     PenPat(QUICKDRAWWHITE);                                            // draw the grid.
  131.     SetOrigin(-xloc, -yloc);
  132.     for (ix = 0; ix <= xsize; ix++) {
  133.         MoveTo (xc + ix * unit, yc);
  134.         Line (0, unit * ysize);
  135.     }
  136.     for (ix = 0; ix <= ysize; ix++) {
  137.         MoveTo (xc, yc + ix * unit);
  138.         Line (unit * xsize, 0);
  139.     }
  140.     SetOrigin(0, 0);
  141.     
  142.     nextDraw = 0;
  143.     
  144.     return noErr;
  145. }
  146.  
  147. // The DoRow and Play routines are Morpion's IA. They are not part of the fader's interface with DarkSide.
  148.  
  149. void DoRow (short bx, short by, short dx, short dy)
  150. {
  151.     short     i;
  152.     short     x, y;
  153.     short     nfriend, nenemy;
  154.     char    g;
  155.     
  156.     for (nfriend = nenemy = 0, x = bx, y = by, i = 5; i; x += dx, y += dy, i--) {
  157.         if ((g = Grid(x, y)) == turn)                                // count how many enemy/friendly markers
  158.             nfriend++;                                        // are present in those five squares
  159.         else if (g != empty)
  160.             nenemy++;
  161.     }
  162.     
  163.     for (x = bx, y = by, i = 5; i; x += dx, y += dy, i--)                    // increment the strategic value of the free squares
  164.         if (Grid(x, y) == empty)                                    // depending on nenemy and nfriendly
  165.             Points(x, y) += value [nfriend][nenemy];
  166. }
  167.  
  168. Point Play (void)
  169. {
  170.     short    ix, iy, npos, max, n, p;
  171.     
  172.     for (ix = 0; ix < xsize; ix++)                                    // Only allow empty spots to be played
  173.         for (iy = 0; iy < ysize; iy++)
  174.             Points(ix, iy) = Grid(ix, iy) == empty ? 0 : -1;
  175.                 
  176.     for (ix = 0; ix < xsize-4; ix++)                                    // Walk the whole grid, giving more points to each
  177.         for (iy = 0; iy < ysize; iy++)                                // interesting spot
  178.             DoRow (ix, iy, 1, 0);
  179.     for (ix = 0; ix < xsize; ix++)
  180.         for (iy = 0; iy < ysize-4; iy++)
  181.             DoRow (ix, iy, 0, 1);
  182.     for (ix = 0; ix < xsize-4; ix++)
  183.         for (iy = 0; iy < ysize-4; iy++)
  184.             DoRow (ix, iy, 1, 1);
  185.     for (ix = 0; ix < xsize-4; ix++)
  186.         for (iy = 4; iy < ysize; iy++)
  187.             DoRow (ix, iy, 1, -1);
  188.  
  189.     for (npos = 0, max = -1, ix = 0; ix < xsize; ix++)                    // Look for the most interesting spot
  190.         for (iy = 0; iy < ysize; iy++) {
  191.             p = Points(ix, iy);
  192.             if (p > max) {                                        // If this square is more interesting than all those
  193.                 max = p; npos = 1;                                // previously visited, forget about them
  194.                 Possible (0).h = ix;
  195.                 Possible (0).v = iy;
  196.             }
  197.             else if (p == max) {                                    // If it is equally interesting than the best previously
  198.                 npos++;                                        // visited squares, add it to the array of possible
  199.                 Possible (npos-1).h = ix;                            // moves.
  200.                 Possible (npos-1).v = iy;
  201.             }
  202.         }
  203.     
  204.     if (max == 0)                                                // Nothing interesting : this is a draw
  205.         draw = true;
  206.     else if (npos) {                                                
  207.         n = Random();                                            // Randomly choose a square from the list
  208.         if (n < 0) n = -n;                                        // of best possible squares
  209.         n = n % npos;
  210.  
  211.         if (max >= value [4][0])                                    // This means we just won
  212.             winner = true;
  213.  
  214.         return Possible (n);                                        // Pick up a solution randomly from the set of
  215.     }                                                        // the best solutions
  216.     else
  217.         draw = true;                                            // The grid is full
  218. }
  219.  
  220. // Called regularly. This is the heart of the fader. Here we do whatever we please...
  221.  
  222. OSErr    IdleFader(MachineInfoPtr machineInfo)
  223. {
  224.     Point            location;
  225.     Rect            r;
  226.     long            finalTicks;
  227.     short        a;
  228.     
  229.     // if it isn't time to draw yet, we return -- we do this rather than delay to avoid
  230.     // locking up the machine with a function as mundane as a screensaver
  231.     if (TickCount() < nextDraw)
  232.         return(noErr);
  233.     
  234.     location = Play();                                            // find out where we want to play
  235.  
  236.     if (!draw) {                                                // If the player actually played something
  237.         SetOrigin(-xloc, -yloc);
  238.         r.left = xc + border + unit * location.h;
  239.         r.top = yc + border + unit * location.v;
  240.         r.right = r.left + unit - 2 * border + 1;
  241.         r.bottom = r.top + unit - 2 * border + 1;
  242.         if (turn == player1)
  243.             EraseOval (&r);                                    // draw the new marker in the proper color
  244.         else {
  245.             PenPat (QUICKDRAWWHITE);
  246.             FrameOval (&r);
  247.             PenPat (QUICKDRAWBLACK);
  248.         }
  249.         (void) PlayResourceSnd (machineInfo, 128, true);                // play sound asynchronously
  250.         for (a = 4; a; a--) {                                        // make it blink so that the user sees it
  251.             InvertRect (&r);
  252.             Delay (2, &finalTicks);
  253.         }
  254.         
  255.         SetOrigin(0, 0);
  256.     }
  257.  
  258.     // calculate the next time to draw    
  259.     nextDraw = TickCount() + 60 - machineInfo->faderSettings->theShorts[0];
  260.  
  261.     if (draw || winner) {                                            // if the game's over
  262.         Str255    resultString;
  263.         
  264.         PenPat (QUICKDRAWBLACK);
  265.         TextMode (srcXor);
  266.         r = machineInfo->theScreens[0].bounds;
  267.         MoveTo (r.left+2*unit, r.top + 2*unit);                        // print a message explaining why
  268.         if (draw)
  269.             {
  270.             GetIndString(resultString, 5000, 1);
  271.             }
  272.         else 
  273.             {
  274.             if (turn == player1)
  275.                 GetIndString(resultString, 5000, 2);
  276.             else
  277.                 GetIndString(resultString, 5000, 3);
  278.             }
  279.         DrawString(resultString);
  280.         PenNormal();
  281.         
  282.         Delay (240, &finalTicks);                                    // wait a few seconds
  283.         StartGame();                                            // and start a new game
  284.         InitializeFader(machineInfo);
  285.         return noErr;
  286.     }
  287.     
  288.  
  289.     Grid(location.h, location.v) = turn;                                // update the grid in memory
  290.     turn = (turn == player1) ? player2 : player1;                        // change turn
  291.         
  292.     return noErr;
  293. }
  294.  
  295. // Called when the fade is tearing down
  296.  
  297. OSErr    DisposeFader(MachineInfoPtr machineInfo)
  298. {
  299. #pragma unused (machineInfo)
  300.  
  301.     DisposHandle (grid);                                            // release all the memory we reserved
  302.     DisposHandle (points);
  303.     DisposHandle (possible);
  304.     return noErr;
  305. }
  306.  
  307. // Called when there is an update event for our fade window.
  308.  
  309. OSErr    UpdateFader(MachineInfoPtr machineInfo)
  310. {
  311.     InitializeFader(machineInfo);                                    // erase the screen, draw the grid
  312.     return noErr;
  313. }
  314.  
  315. // Called when there is an events in the settings dialog. itemHit will be the item the user has selected, or 0 when the dialog
  316. // is being set up. 
  317. // itemHit - itemOffset will allow you to determine which item in your dialog list this corresponds to.
  318. // If you don't wish to do any special processing of this event, simply return fnfErr and the standard effect will take place.
  319.  
  320. OSErr    HitFader(MachineInfoPtr machineInfo, DialogPtr dPtr, short itemHit, short itemOffset)
  321. {
  322. #pragma unused (machineInfo, dPtr, itemHit, itemOffset)
  323.  
  324.     return fnfErr;
  325. }
  326.